using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Configuration;
using System.Runtime.InteropServices;
using AzureContrib.WindowsAzure.Properties;
using AzureContrib.WindowsAzure.Utils;
using Microsoft.WindowsAzure.ServiceRuntime;
using MSRoleEnvironment = Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment;
using MSRoleEnvironmentException = Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironmentException;
using SD = System.Diagnostics;

namespace AzureContrib.WindowsAzure.ServiceRuntime
{
	/// <summary>
	/// A small manager class to easily and safely access the <see cref="Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue"/>
	/// </summary>
	[Export(typeof(IRoleEnvironment))]
	public class RoleEnvironment: IRoleEnvironment
	{
		/// <summary>
		/// The real wrapped RoleEnvironment that is called. This may be mocked in tests
		/// </summary>
		public IRoleEnvironment RealRoleEnvironment { set; internal get; }

		/// <summary>
		/// Ctor
		/// </summary>
		public RoleEnvironment()
		{
			RealRoleEnvironment = new WrappedRoleEnvironment();
		}

		/// <summary>
		/// Get a configuration setting from <see cref="Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue"/>
		/// </summary>
		/// <param name="configurationSettingName">The name of the configuration setting to fetch.</param>
		/// <returns>The configuration setting value.</returns>
		public string GetConfigurationSettingValue(string configurationSettingName)
		{
			return RealRoleEnvironment.IsAvailable ?
						 RealRoleEnvironment.GetConfigurationSettingValue(configurationSettingName) :
						 ConfigurationManager.AppSettings[configurationSettingName];
		}

		/// <summary>
		/// Get a configuration setting from <see cref="Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue"/>
		/// </summary>
		/// <param name="configurationSettingName">The name of the configuration setting to fetch.</param>
		/// <param name="settingValue">The configuration setting value.</param>
		/// <returns>True if the setting is present.</returns>
		public bool TryGetConfigurationSettingValue(string configurationSettingName, out string settingValue)
		{
			if (RealRoleEnvironment.IsAvailable)
			{
				return RealRoleEnvironment.TryGetConfigurationSettingValue(configurationSettingName, out settingValue);
			}

			settingValue = ConfigurationManager.AppSettings[configurationSettingName];

			return settingValue != null;
		}

		/// <summary>
		/// Wrapper for <see cref="Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.Changing"/>.
		/// </summary>
		public event EventHandler<RoleEnvironmentChangingEventArgs> Changing
		{
			add { RealRoleEnvironment.Changing += value; }
			remove { RealRoleEnvironment.Changing -= value; }
		}

		/// <summary>
		/// Wrapper for <see cref="Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.Changed"/>.
		/// </summary>
		public event EventHandler<RoleEnvironmentChangedEventArgs> Changed
		{
			add { RealRoleEnvironment.Changed += value; }
			remove { RealRoleEnvironment.Changed -= value; }
		}

		/// <summary>
		/// The Id of the current Azure deployment.
		/// </summary>
		public string DeploymentId
		{
			get { return RealRoleEnvironment.DeploymentId; }
		}

		/// <summary>
		/// <see cref="Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.CurrentRoleInstance"/>
		/// </summary>
		public IRoleInstance CurrentRoleInstance
		{
			get { return RealRoleEnvironment.CurrentRoleInstance; }
		}

		/// <summary>
		/// Is the RoleEnvironment available?
		/// </summary>
		public bool IsAvailable
		{
			get { return RealRoleEnvironment.IsAvailable; }
		}

		/// <summary>
		/// Returns a reference to a <see cref="ILocalResource"/>
		/// </summary>
		/// <param name="localResourceName"></param>
		/// <returns></returns>
		public ILocalResource GetLocalResource(string localResourceName)
		{
			return RealRoleEnvironment.GetLocalResource(localResourceName);
		}

		internal class WrappedRoleEnvironment : IRoleEnvironment
		{
			public string DeploymentId
			{
				get { return MSRoleEnvironment.DeploymentId; }
			}

			public bool IsAvailable
			{
				get
				{
					return MSRoleEnvironment.IsAvailable;
				}
			}

			public IDictionary<string, IRole> Roles
			{
				get
				{
					var dictionary = new Dictionary<string, IRole>();
					MSRoleEnvironment.Roles.ForEach(kvp => dictionary.Add(kvp.Key, new RoleWrapper(kvp.Value)));
					return dictionary;
				}
			}

			public event EventHandler<RoleEnvironmentChangedEventArgs> Changed
			{
				add { MSRoleEnvironment.Changed += value; }
				remove { MSRoleEnvironment.Changed -= value; }
			}

			public event EventHandler<RoleEnvironmentChangingEventArgs> Changing
			{
				add { MSRoleEnvironment.Changing += value; }
				remove { MSRoleEnvironment.Changing -= value; }
			}

			public string GetConfigurationSettingValue(string roleSettingName)
			{
				string setting;
				try
				{
					setting = MSRoleEnvironment.GetConfigurationSettingValue(roleSettingName);
				}
				catch (MSRoleEnvironmentException)
				{
					SD.Trace.TraceWarning(Strings.RoleSettingNonExistant, roleSettingName);
					setting = null;
				}
				catch (SEHException)
				{
					SD.Trace.TraceWarning(Strings.RoleSettingNonExistant, roleSettingName);
					setting = null;
				}
				return setting;
			}

			/// <summary>
			/// Get a configuration setting from <see cref="Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue"/>
			/// </summary>
			/// <param name="configurationSettingName">The name of the configuration setting to fetch.</param>
			/// <param name="settingValue">The configuration setting value.</param>
			/// <returns>True if the setting is present.</returns>
			public bool TryGetConfigurationSettingValue(string configurationSettingName, out string settingValue)
			{
				try
				{
					settingValue = MSRoleEnvironment.GetConfigurationSettingValue(configurationSettingName);
				}
				catch (SEHException)
				{
					// When not in Azure or Azure emulator
					settingValue = null;
				}
				catch (MSRoleEnvironmentException)
				{
					// When in Azure or Azure emulator
					settingValue = null;
				}
				catch (Exception ex)
				{
					SD.Debug.WriteLine(ex);
					settingValue = null;
				}
				return settingValue != null;
			}

			public ILocalResource GetLocalResource(string localResourceName)
			{
				try
				{
					LocalResource localResource = MSRoleEnvironment.GetLocalResource(localResourceName);
					LocalResourceWrapper localResourceWrapper = new LocalResourceWrapper(localResource);
					return localResourceWrapper;
				}
				catch (SEHException sehe)
				{
					var message = Strings.UnableToAccessLocalResource.F(localResourceName);
					SD.Trace.TraceError(message);
					throw new RoleEnvironmentException(message, sehe);
				}
			}

			public void RequestRecycle()
			{
				MSRoleEnvironment.RequestRecycle();
			}

			public IRoleInstance CurrentRoleInstance
			{
				get { return new RoleInstanceWrapper(MSRoleEnvironment.CurrentRoleInstance); }
			}
		}
	}
}